home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Pascal / Snippets / Marquee / Marquee.p < prev    next >
Encoding:
Text File  |  1995-08-13  |  10.0 KB  |  368 lines  |  [TEXT/MMCC]

  1. {
  2.  
  3. This program is based on Finder Marquee by Jordan Zimmerman.
  4.  
  5. Using the techniques in the original C program, this program is 
  6. done in Pascal.  It uses a linked lists for objects instead of
  7. an array, and a new function was added for adding objects (Add_square).
  8. Some of the other data structures were also changed.
  9.  
  10. This code implements a "rubber band" marquee select rect
  11. with very smooth drawing in a manner similar to the Mac Finder.
  12.     
  13. Project includes:
  14.  
  15. Marquee.p  - Pascal source code
  16. Marquee.µ  - Codewarrior Pascal project file.
  17.     
  18. 8/13/95 - Bill Catambay, catambay@aol.com
  19.  
  20. }
  21.  
  22. Program Marquee;
  23.  
  24. Uses
  25.     Types, Windows, Events, Fonts, Dialogs, TextUtils;
  26.  
  27. Type
  28.     finder_marquee_rec =    record
  29.         marquee_r:                Rect;    { current marquee_r }
  30.         pin_pt:                    Point;        { mouse down point }
  31.         current_pt:                Point;    { current mouse location }
  32.         end;
  33.     marquee_ptr_type = ^finder_marquee_rec;
  34.     Highlight_rec    =    record
  35.         bounds_r:        Rect;
  36.         selected_flag:    boolean;
  37.         next_rec:        ^highlight_rec;
  38.         end;
  39.     Highlight_ptr    = ^highlight_rec;
  40.  
  41. Var
  42.     bounds_r:    Rect;
  43.     mainw:        WindowPtr;
  44.     statusw:    WindowPtr;
  45.     is_done:    boolean;
  46.     the_event:    EventRecord;
  47.     menu:        menuRef;
  48.     font_num:    integer;
  49.     tab_ptr:    highlight_ptr;
  50.  
  51. Function selections_proc(marquee_ptr:    marquee_ptr_type): boolean;
  52.  
  53. Var
  54.     i,qty:        integer;
  55.     sect_r:        rect;
  56.     tmp:        highlight_ptr;
  57.     
  58.     begin
  59.     tmp := tab_ptr;
  60.     while tmp <> NIL do
  61.         begin
  62.         if SectRect(marquee_ptr^.marquee_r, tmp^.bounds_r, sect_r) <> tmp^.selected_flag then
  63.             begin
  64.             selections_proc := TRUE;
  65.             exit(selections_proc);
  66.             end;
  67.         tmp := tmp^.next_rec;
  68.         end;
  69.     selections_proc := false;
  70.     end;
  71.     
  72. { invert any of the highlight rects that intersect the current marquee rect }
  73. Procedure change_selection_proc(marquee_ptr:        marquee_ptr_type;
  74.                                 old_marquee_r:        Rect);
  75.  
  76. Var
  77.     i,qty:        integer;
  78.     tmp:        highlight_ptr;
  79.     sect_r:        rect;
  80.  
  81.     begin
  82.     tmp := tab_ptr;
  83.     while tmp <> NIL do
  84.         begin
  85.         if SectRect(marquee_ptr^.marquee_r, tmp^.bounds_r, sect_r) <> tmp^.selected_flag then
  86.             begin
  87.             tmp^.selected_flag := not tmp^.selected_flag;
  88.             InvertRect(tmp^.bounds_r);
  89.             end;
  90.         tmp := tmp^.next_rec;
  91.         end;
  92.     end;
  93.  
  94. Function num2str(num:    integer): string;
  95.  
  96. Var
  97.     str:    str255;
  98.     
  99.     begin
  100.     numTostring(num,str);
  101.     num2str := str;
  102.     end; { of num2str }
  103.  
  104. { outline the marquee rect in Xor gray }
  105. Procedure draw_marquee_r(marquee_ptr: marquee_ptr_type);
  106.  
  107. Var
  108.     pen_state: penstate;
  109.     
  110.     begin
  111.     GetPenState(pen_state);
  112.     PenMode(patXor);    
  113.     PenPat(qd.gray);
  114.     FrameRect(marquee_ptr^.marquee_r);
  115.     SetPenState(pen_state);
  116.     end;
  117.  
  118. { calculating the marquee rect isn't as simple as Pt2Rect.  Using Pt2Rect }
  119. { causes the pin point to shift around.  This function will calculate a }
  120. { correct marquee rect that keeps the pin point in place  }
  121. Procedure calculate_marquee_r(marquee_ptr: marquee_ptr_type);
  122.  
  123.     begin
  124.     if (marquee_ptr^.current_pt.h >= marquee_ptr^.pin_pt.h) & 
  125.        (marquee_ptr^.current_pt.v >= marquee_ptr^.pin_pt.v) then    { Quadrant IV }
  126.         SetRect(marquee_ptr^.marquee_r, marquee_ptr^.pin_pt.h, marquee_ptr^.pin_pt.v, marquee_ptr^.current_pt.h + 1, 
  127.             marquee_ptr^.current_pt.v + 1)
  128.     else if (marquee_ptr^.current_pt.h <= marquee_ptr^.pin_pt.h) & 
  129.             (marquee_ptr^.current_pt.v <= marquee_ptr^.pin_pt.v) then    { Quadrant I }
  130.         SetRect(marquee_ptr^.marquee_r, marquee_ptr^.current_pt.h, marquee_ptr^.current_pt.v, marquee_ptr^.pin_pt.h + 1, 
  131.             marquee_ptr^.pin_pt.v + 1)
  132.     else if (marquee_ptr^.current_pt.h >= marquee_ptr^.pin_pt.h) & 
  133.             (marquee_ptr^.current_pt.v <= marquee_ptr^.pin_pt.v) then    { Quadrant II }
  134.         SetRect(marquee_ptr^.marquee_r, marquee_ptr^.pin_pt.h, marquee_ptr^.current_pt.v, marquee_ptr^.current_pt.h + 1, 
  135.             marquee_ptr^.pin_pt.v + 1)
  136.     else { Quadrant III }
  137.         SetRect(marquee_ptr^.marquee_r, marquee_ptr^.current_pt.h, marquee_ptr^.pin_pt.v, marquee_ptr^.pin_pt.h + 1, 
  138.             marquee_ptr^.current_pt.v + 1);
  139.     end;
  140.  
  141. Procedure FinderMarqueeBegin(marquee_ptr:    marquee_ptr_type; 
  142.                             mouse_down_pt:    Point);
  143.  
  144. Var
  145.     old_marquee_r:    rect;
  146.     
  147.     begin
  148.     marquee_ptr^.pin_pt := mouse_down_pt;
  149.     marquee_ptr^.current_pt := mouse_down_pt;
  150.     calculate_marquee_r(marquee_ptr);
  151.     change_selection_proc(marquee_ptr, marquee_ptr^.marquee_r);
  152.     draw_marquee_r(marquee_ptr);
  153.     end;
  154.  
  155. { utility to make a 1 pixel thick region of the frame outline of a rect }
  156. Procedure make_frame_region(target_rgn_h: RgnHandle;
  157.                             frame_r: Rect;
  158.                             work_rgn_h: RgnHandle);
  159.  
  160.     begin
  161.     RectRgn(target_rgn_h, frame_r);
  162.     CopyRgn(target_rgn_h, work_rgn_h);
  163.     InsetRgn(work_rgn_h, 1, 1);
  164.     DiffRgn(target_rgn_h, work_rgn_h, target_rgn_h);
  165.     end;
  166.     
  167. Procedure FinderMarqueeContinue(marquee_ptr:    marquee_ptr_type;
  168.                                 new_pt:            Point);
  169.  
  170. Var
  171.     pen_state:        PenState;
  172.     old_marquee_r:    Rect;
  173.     old_rgn_h:        RgnHandle;
  174.     work_rgn_h:        RgnHandle;
  175.     new_rgn_h:        RgnHandle;
  176.     clip_rgn_h:        RgnHandle;
  177.     success_flag:    boolean;
  178.  
  179.     begin
  180.     old_rgn_h := NIL;
  181.     work_rgn_h := NIL;
  182.     new_rgn_h := NIL;
  183.     clip_rgn_h := NIL;
  184.     success_flag := false;
  185.     { avoid flashing step 1 - do nothing if the mouse hasn't moved }
  186.     if EqualPt(new_pt, marquee_ptr^.current_pt) then
  187.         exit(FinderMarqueeContinue);
  188.     clip_rgn_h := NewRgn;
  189.     if clip_rgn_h = NIL then
  190.         exit(FinderMarqueeContinue);    { can't find 10 bytes!  We're probably already in big trouble }
  191.     { we'll be messing with the clip, so save it for later restoration }
  192.     GetClip(clip_rgn_h);
  193.     GetPenState(pen_state);
  194.     PenMode(patXor);
  195.     PenPat(qd.gray);
  196.     { save the old marquee_r and setup the new one }
  197.     old_marquee_r := marquee_ptr^.marquee_r;
  198.     marquee_ptr^.current_pt := new_pt;
  199.     calculate_marquee_r(marquee_ptr);
  200.     repeat
  201.         old_rgn_h := NewRgn;
  202.         if old_rgn_h = NIL then
  203.             leave;
  204.         work_rgn_h := NewRgn;
  205.         if work_rgn_h = NIL then
  206.             leave;
  207.         new_rgn_h := NewRgn;
  208.         if new_rgn_h = NIL then
  209.             leave;
  210.         { generate 1 pixel thick outline regions of the old and new marquee_r }
  211.         make_frame_region(old_rgn_h, old_marquee_r, work_rgn_h);
  212.         make_frame_region(new_rgn_h, marquee_ptr^.marquee_r, work_rgn_h);
  213.         { get the area in common between the old and the new }
  214.         SectRgn(old_rgn_h, new_rgn_h, work_rgn_h);
  215.         { set the clip to the old clip minus the common area of the old and new marquee rect }         
  216.         DiffRgn(clip_rgn_h, work_rgn_h, work_rgn_h);
  217.         SetClip(work_rgn_h);                            
  218.         if selections_proc(marquee_ptr) then
  219.             begin
  220.             { If there is a selection, the old marquee must be erased, the selections must be drawn, }
  221.             { and then the new marquee can be drawn. }
  222.             FrameRect(old_marquee_r);
  223.             change_selection_proc(marquee_ptr, old_marquee_r);
  224.             FrameRect(marquee_ptr^.marquee_r);
  225.             end
  226.         else
  227.             begin
  228.             { If there's no selection change, the marquee can be drawn in one step }
  229.             { that will erase the old and draw the new. }
  230.             UnionRgn(new_rgn_h, old_rgn_h, work_rgn_h);
  231.             PaintRgn(work_rgn_h);    
  232.             end;
  233.         success_flag := true;
  234.     until success_flag;    { i.e. do once, set success_flag on exit }
  235.     if not success_flag then
  236.         begin
  237.         { memory is evidently very tight, we'll have to live with flashing }
  238.         FrameRect(old_marquee_r);
  239.         change_selection_proc(marquee_ptr, old_marquee_r);
  240.         FrameRect(marquee_ptr^.marquee_r);
  241.         end;
  242.     SetClip(clip_rgn_h);
  243.     SetPenState(pen_state);
  244.     if old_rgn_h <> NIL then
  245.         DisposeRgn(old_rgn_h);
  246.     if work_rgn_h <> NIL then
  247.         DisposeRgn(work_rgn_h);
  248.     if new_rgn_h <> NIL then
  249.         DisposeRgn(new_rgn_h);
  250.     if clip_rgn_h <> NIL then
  251.         DisposeRgn(clip_rgn_h);
  252.     SetPort(statusw);
  253.     EraseRect(statusw^.portRect);
  254.     with statusw^.portRect do 
  255.         MoveTo(left + 5, top + 10);
  256.     textsize(9);
  257.     textfont(font_num);
  258.     with marquee_ptr^.marquee_r do
  259.         drawstring(concat('Size: ',num2str(bottom - top),', ',num2str(right - left)));
  260.     with statusw^.portRect do 
  261.         MoveTo(left + 5, top + 24);
  262.     drawstring(concat('Mouse: ',num2str(new_pt.h),', ',num2str(new_pt.v)));
  263.     SetPort(mainw);
  264.     end;
  265.  
  266. { here's the meat of the program }
  267. { while this example does it all in one function, }
  268. { you could just as easily separate the various calls and do it in the background }
  269. Procedure do_marquee(local_pt:    Point);
  270.  
  271. Var
  272.     marquee:    finder_marquee_rec;
  273.     new_pt:        Point;
  274.     
  275.     begin
  276.     FinderMarqueeBegin(@marquee, local_pt);
  277.     while StillDown do with marquee.marquee_r do
  278.         begin
  279.         GetMouse(new_pt);
  280.         FinderMarqueeContinue(@marquee, new_pt);
  281.         end;
  282.     draw_marquee_r(@marquee);
  283.     end;
  284.  
  285. { walk through our highlight rects and see if any of the selections will change. }
  286. { the marquee drawing must work slightly differently if there's going to be }
  287. { a selection change. }
  288. { If there's no selection change, the marquee can be drawn in one step (PaintRgn) }
  289. { that will erase the old and draw the new.  If there is a selection, though, the }
  290. { old marquee must be erased, the selections must be drawn, and then the new marquee }
  291. { can be drawn. }
  292. { draw in response to an update event }
  293. Procedure draw_highlights;
  294.  
  295. Var
  296.     i,qty:        integer;
  297.     tmp:        highlight_ptr;
  298.  
  299.     begin
  300.     EraseRect(mainw^.portRect);
  301.     tmp := tab_ptr;
  302.     while tmp <> NIL do
  303.         begin
  304.         FrameRect(tmp^.bounds_r);
  305.         if tmp^.selected_flag then
  306.             InvertRect(tmp^.bounds_r);
  307.         tmp := tmp^.next_rec;
  308.         end;
  309.     end;
  310.  
  311. Procedure Add_square(left,top,right,bottom:    integer);
  312.  
  313. Var
  314.     tmp:    highlight_ptr;
  315.     
  316.     begin
  317.     new(tmp);
  318.     SetRect(tmp^.bounds_r,left, top, right, bottom);
  319.     tmp^.selected_flag := false;
  320.     tmp^.next_rec := tab_ptr;
  321.     tab_ptr := tmp;
  322.     disposePtr(pointer(tmp));
  323.     end;
  324.     
  325. begin
  326. is_done := false;
  327. MaxApplZone;
  328. InitGraf(@qd.thePort);
  329. InitFonts;
  330. FlushEvents(everyEvent, 0);
  331. InitWindows;
  332. InitMenus;
  333. TEInit;
  334. InitDialogs(NIL);
  335. InitCursor;
  336. textsize(9);
  337. getfnum('Monaco',font_num);
  338. textfont(font_num);
  339. tab_ptr := NIL;
  340. add_square(10, 10, 42, 42);
  341. add_square(50, 75, 82, 107);
  342. add_square(100, 10, 132, 42);
  343. add_square(110, 200, 142, 232);
  344. add_square(308, 308, 340, 340);
  345. menu := NewMenu(1, 'Finder Marquee');
  346. InsertMenu(menu, 0);
  347. DrawMenuBar;
  348. SetRect(bounds_r, 50, 50, 400, 400);
  349. mainw := NewCWindow(NIL, bounds_r, 'Press Any Key To Quit', true, noGrowDocProc, WindowPtr(-1), false, 0);
  350. SetRect(bounds_r, 100, 410, 200, 450);
  351. statusw := NewCWindow(NIL, bounds_r, '', true, plaindbox, NIL, false, 0);
  352. SetPort(mainw);
  353. while not is_done do
  354.     if WaitNextEvent(everyEvent, the_event, GetDblTime, NIL) then
  355.         case the_event.what of
  356.             keyDown,
  357.             autoKey:    is_done := true;
  358.             updateEvt:    begin
  359.                         BeginUpdate(mainw);
  360.                         draw_highlights;
  361.                         EndUpdate(mainw);
  362.                         end;
  363.             mouseDown:    begin
  364.                         GlobalToLocal(the_event.where);
  365.                         do_marquee(the_event.where);
  366.                         end;
  367.         {CASE}    end;
  368. end.